<?php


namespace App\Service\PayApi;

use App\Model\Dao\BankInfoDao;
use App\Model\Dao\ChannelInfoDao;
use App\Model\Dao\TradeOrderBaseDao;
use App\Model\Dao\TradeOrderDetailDao;
use App\Model\Enum\BankTypeEnum;
use App\Model\Enum\EmailSendTypeEnum;
use App\Model\Enum\OrderTransStatusEnum;
use App\Model\Enum\OrderBaseDescEnum;
use App\Service\CommonBankService;
use App\Service\CommonRiskService;
use App\Service\EmailSendInfoService;
use App\Service\PayApi\Callback\PayCallbackService;
use App\Utils\CustomUtils;
use App\Utils\RedisUtils;
use App\Utils\SignUtils;
use App\Utils\StringUtils;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Utils\Arr;
use Hyperf\Utils\Coroutine;

/**
 * Class BankPayService
 *
 * @package App\Service\PayApi
 */
class BankPayService {

    /**
     * @var ChannelInfoDao
     * @Inject()
     */
    protected $ChannelInfoDao;

    /**
     * @var BankInfoDao
     * @Inject()
     */
    protected $BankInfoDao;

    /**
     * @var TradeOrderBaseDao
     * @Inject()
     */
    protected $TradeOrderBaseDao;

    /**
     * @var TradeOrderDetailDao
     * @Inject()
     */
    protected $TradeOrderDetailDao;


    /**
     * @var CommonBankService
     * @Inject()
     */
    protected $CommonBankService;

    /**
     * @var TradeOrderService
     * @Inject()
     */
    protected $TradeOrderService;


    /**
     * @param $mch
     * @param $order
     * @param $payType
     * @param $payAccount
     * @param $dispatchChannel
     *
     * @return array
     * @throws \Exception
     */
    public function handle ($mch, $order, $payType, $payAccount, $dispatchChannel) {

        $transTime = time();

        $orderDetail  = $order['orderDetail'];
        $orderBase    = $order['orderBase'];
        $orderReceive = $order['orderReceive'];

        // 更新交易跟踪表
        $this->TradeOrderBaseDao->set($orderBase['id'], ['description' => OrderBaseDescEnum::UPLOAD]);

        // 查询风控调度得到的通道，银行信息
        $channel = $this->ChannelInfoDao->get($dispatchChannel['channelId']);
        $bank    = $this->BankInfoDao->get($channel['bankId']);

        $channelParam = json_decode($channel['params'], true);

        // TODO::对于channelParam,按照需求需要进行处理
        $req = [
            'channelNo'    => $channel['channelNo'],
            // TODO::环境参数
            'environment'  => [
            ],
            'trans'        => [
                'tradeNo'       => $orderDetail['tradeNo'],
                'transCurrency' => $dispatchChannel['channelTransCurrency'],
                'transAmount'   => $dispatchChannel['channelTransAmount'],
                'time'          => $transTime,
            ],
            'payType'      => $payType,
            'payAccount'   => $payAccount,
            'channelParam' => $channelParam,
        ];

        // 调用银行网关提供的接口
        $reBankPay = $this->CommonBankService->doPay($req);

        CustomUtils::xzxDebug("银行网关接口返回", $reBankPay);

        // 更新订单表中,上报通道银行的金额,币种
        $updateOrder = [
            'channelTransCurrency' => $dispatchChannel['channelTransCurrency'],
            'channelTransAmount'   => $dispatchChannel['channelTransAmount'],
            'channelTransTime'     => $transTime,
            'channelId'            => $channel['id'],
            'channelName'          => $channel['channelName'],
            'bankId'               => $bank['id'],
            'bankName'             => $bank['bankName'],
        ];

        // 状态变更
        if (CustomUtils::isSuccess($reBankPay)) {
            $updateOrder = array_merge($updateOrder, [
                'transStatus' => OrderTransStatusEnum::BANK_SEND_SUCCESS,
                'relNo'       => $reBankPay['data']['relNo'],
            ]);
        }
        else {
            $updateOrder = array_merge($updateOrder, [
                'transStatus' => $reBankPay['data']['isBankFail'] ? OrderTransStatusEnum::BANK_SEND_ERROR : OrderTransStatusEnum::SERVICE_ERROR,
            ]);
        }


        // 特殊银行返回结果处理
        $updateOrder = $this->_extraBankPayUpdateOrder($updateOrder, $orderDetail, $reBankPay, $bank, $channel);

        // 订单详情更新
        $this->TradeOrderDetailDao->update($orderDetail['id'], $updateOrder);

        // 交易跟踪
        if (CustomUtils::isSuccess($reBankPay)) {
            $this->TradeOrderBaseDao->update($orderBase['id'], [
                'bankRespMsg' => json_encode($reBankPay, JSON_UNESCAPED_UNICODE),
            ]);
        }
        else {

            // 这里的反馈码做个处理， 反馈码 = 银行编号（4位）-银行反馈码 TODO 确认处理规则
            $code    = $bank['bankNo'].(isset($reBankPay['data']['code']) && is_numeric($reBankPay['data']['code']) ? $reBankPay['data']['code'] : 8888);
            $message = "[上送银行提示] {$reBankPay["message"]}";
            $this->TradeOrderService->setOrderBaseFail($orderDetail['tradeNo'], $code, $message, true);
        }

        // 限额回滚
        if (CustomUtils::isError($reBankPay)) {
            $this->_rollbackChannelLimitCost($orderDetail['mchNo'], $dispatchChannel['channelNo'], $dispatchChannel['channelLimitAmount']);
        }

        // 对返回数据的额外处理
        $reBankPay = $this->_extraBankPayResultHandle($reBankPay, $mch, $bank['type']);

        return $reBankPay;
    }

    /**
     * 部分银行需要将返回值进行特殊处理
     *
     * @param $reBankPay
     * @param $mch
     * @param $type
     */
    protected function _extraBankPayResultHandle ($reBankPay, $mch, $type) {
        // 目前没什么需要处理的
        return $reBankPay;
    }

    /**
     * 部分银行需要根据返回值, 对订单更新字段进行特殊处理
     *
     * @param $updateOrder
     * @param $orderDetail
     * @param $reBankPay
     * @param $bank
     * @param $channel
     *
     * @return mixed
     */
    protected function _extraBankPayUpdateOrder ($updateOrder, $orderDetail, $reBankPay, $bank, $channel) {

        // 不同银行类型
        switch ($bank['type']) {

            // 测试银行
            case BankTypeEnum::TEST:
                $updateOrder['isTest']      = true;
                if (CustomUtils::isSuccess($reBankPay)) {
                    $updateOrder['transStatus'] = OrderTransStatusEnum::BANK_CALLBACK_SUCCESS;

                    // 5秒后模拟回调逻辑
                    co(function () use ($reBankPay) {
                        $relNo = Arr::get($reBankPay, 'data.relNo');
                        CustomUtils::xzxDebug('将在3秒后模拟触发回调逻辑, relNo: ' . $relNo);
                        Coroutine::sleep(3);
                        CustomUtils::xzxDebug('模拟触发回调逻辑, relNo: ' . $relNo);
                        ApplicationContext::getContainer()->get(PayCallbackService::class)->handle([
                            'relNo' => $relNo,
                        ]);
                    });
                }


                break;
        }

        return $updateOrder;
    }

    /**
     * 请求网关模块
     *
     * @param $riskData
     * @param $orderDetail
     * @param $orderBase
     * @param $orderReceive
     * @param $cardSecretInfo
     * @param $mchToPlatform
     * @param $platformToBank
     * @param $mch
     * @param $bank
     * @param $channel
     *
     * @return array
     */
    protected function _buildReq ($params) {

        $riskData       = $params['riskData'];
        $orderDetail    = $params['orderDetail'];
        $orderBase      = $params['orderBase'];
        $orderReceive   = $params['orderReceive'];
        $cardSecretInfo = $params['cardSecretInfo'];
        $mchToPlatform  = $params['mchToPlatform'];
        $platformToBank = $params['platformToBank'];
        $mch            = $params['mch'];
        $channel        = $params['channel'];
        $bank           = $params['bank'];
        $channelParam   = $params['channelParam'];

        // 结构化参数, 便于理解和维护
        $reqBankPay = [
            'channelNo'    => '',
            'environment'  => [],
            'trans'        => [
                'tradeNo'  => $orderDetail['tradeNo'],
                'currency' => '',
                'amount'   => '',
                'time'     => date('Y-m-d H:i:s'),
            ],
            'payType'      => '',
            'payAccount'   => [

            ],
            'channelParam' => $params['channelParam'],
        ];

        // // 构造银行网关请求参数
        // $reqBankPay                  = [
        //     'tradeNo'    => $orderDetail['tradeNo'],
        //     'webInfo'    => $orderDetail['webInfo'],
        //     'ipAddress'  => $orderDetail['ipAddress'],
        //     'attach'     => $orderDetail['attach'],
        //     'payWebSite' => Arr::get($orderDetail, 'payWebSite'),
        //
        //     'cardType'      => $orderReceive['cardType'],
        //     'cardNo'        => $orderReceive['cardNo'],
        //     'cardCountry'   => $orderReceive['cardCountry'],
        //     'cardState'     => $orderReceive['cardState'],
        //     'cardCity'      => $orderReceive['cardCity'],
        //     'cardAddress'   => $orderReceive['cardAddress'],
        //     'cardFullName'  => $orderReceive['cardFullName'],
        //     'cardFullPhone' => $orderReceive['cardFullPhone'],
        //     'cardZipCode'   => $orderReceive['cardZipCode'],
        //     'cardEmail'     => $orderReceive['cardEmail'],
        //
        //     'grCountry'     => $orderReceive['grCountry'],
        //     'grState'       => $orderReceive['grState'],
        //     'grCity'        => $orderReceive['grCity'],
        //     'grAddress'     => $orderReceive['grAddress'],
        //     'grZipCode'     => $orderReceive['grZipCode'],
        //     'grEmail'       => $orderReceive['grEmail'],
        //     'grPhoneNumber' => $orderReceive['grPhoneNumber'],
        //     'grPerName'     => $orderReceive['grPerName'],
        //     'goodsInfo'     => $orderReceive['goodsInfo'],
        //
        //     'cardExpireY' => $cardSecretInfo['cardExpireY'],
        //     'cardExpireM' => $cardSecretInfo['cardExpireM'],
        //     'cardCvv'     => $cardSecretInfo['cardCvv'],
        //
        //     'httpProtocol' => Arr::get($orderBase, 'transPortProtocol'),
        //     'mchUrl'       => $orderDetail['mchUrl'],
        //     'submitUrl'    => $orderBase['submitUrl'],
        // ];
        // $reqBankPay['bankTransTime'] = date('Y-m-d H:i:s');
        //
        // $reqBankPay['bankCurrency']    = $riskData['channel']['channelTransCurrency'];
        // $reqBankPay['bankTransAmount'] = $riskData['channel']['channelTransAmount'];
        //
        // // 拼上银行参数
        // $reqBankPay['bankHost']      = $bank['host'];
        // $reqBankPay['bankPort']      = $bank['port'];
        // $reqBankPay['bankProxyHost'] = $bank['proxyHost'];
        // $reqBankPay['bankProxyPort'] = $bank['proxyPort'];
        // $reqBankPay['channel']       = $bank['type'];
        //
        // // 拼上通道参数
        // foreach ($channelParam as $k => $v) {
        //     $reqBankPay[$k] = $v;
        //
        //     // 如果是支付完成页面，就追加参数
        //     if ($k == 'callback_url' && !empty($v)) {
        //
        //         $tmp = [
        //             'appId'   => $mch['appId'],
        //             'tradeNo' => $orderDetail['tradeNo'],
        //         ];
        //
        //         $tmp['sign'] = SignUtils::makeSign($tmp, $mch['appSecret']);
        //
        //         $reqBankPay[$k] = $v."?appId={$tmp['appId']}|tradeNo={$tmp['tradeNo']}|sign={$tmp['sign']}";
        //     }
        // }

        return $reqBankPay;
    }

    /**
     * @param $mchNo
     * @param $channelNo
     * @param $amount
     *
     * @throws \Exception
     */
    protected function _rollbackChannelLimitCost ($mchNo, $channelNo, $amount) {

        CustomUtils::xzxDebug('通知限额回滚');

        $data = [
            'mchNo'     => $mchNo,
            'channelNo' => $channelNo,
            'amount'    => $amount,
            'year'      => date('Y'),
            'month'     => date('m'),
            'day'       => date('d'),
        ];

        // 调用风控模块的接口
        ApplicationContext::getContainer()->get(CommonRiskService::class)->recoverChannelLimit($data);
    }

    private function _setOrderBaseFailed ($tradeNo, string $code, string $message, bool $isBankHandle) {
        return ApplicationContext::getContainer()->get(TradeOrderService::class)->setOrderBaseFail($tradeNo, $code, $message, $isBankHandle);
    }

}
